package sketch;

import sketch.generator.AssertionGenerator;
import sketch.generator.ChainValueGenerator;
import sketch.generator.SequenceGenerator;
import sketch.generator.ValueGenerator;
import sketch.skeleton.GeneratorMaps;
import sketch.util.SketchUtil;

public class OracleChoice {
	
	/**
	 * This will enable all oracle observations
	 * */
	public static void observeAllOutputs() {
		
	}
	/***
	 * The following methods produce test assertions
	 * first need to decide the type of receiver, if that is an MethodOutcome object
	 * we need to fetch its outcome object first, if the MethodOutcome object has exception,
	 * we skip that
	 * if the assertion (observation) fails, it throws an exception
	 * */
	public static SequenceGenerator<? extends Object> observeMethodOutput(Object receiver, String methodName) {
		//this produces the assertion like:  assert(receiver.methodName == ?)
		SMethod smethod = SMethod.instance(receiver, methodName);
		return new AssertionGenerator<SketchSequence>(receiver, smethod, new ChainValueGenerator<Object>(0));
	}
	
	public static SequenceGenerator<? extends Object> observeMethodOutput(Object receiver,
			String methodName, Object expectedResult) {
		//this produces the assertion like:  assert(receiver.methodName.equals(expectedResult)
		SMethod smethod = SMethod.instance(receiver, methodName);
		AssertionGenerator<SketchSequence> assertSeq = new AssertionGenerator<SketchSequence>(receiver,
				smethod, new ChainValueGenerator<Object>(0));
		assertSeq.setExpectedObject(expectedResult);
		return assertSeq;
	}
	
	public static SequenceGenerator<? extends Object> observeMethodOutput(Object receiver, String methodName,
			MethodExecOutcome expectedOutcome) {
		//method execution outcome is an abstraction
		//according to the content of expectedOutcome, it could produce assertion like:
		//assert(receiver.methodName.equals(expectedOutcome.getResultObject())
		//try {
		//    receiver.methodName()
		//} catch (expectedException)
		SMethod smethod = SMethod.instance(receiver, methodName);
		AssertionGenerator<SketchSequence> assertSeq = new AssertionGenerator<SketchSequence>(receiver, smethod,
				new ChainValueGenerator<Object>(0));
		assertSeq.setExpectedOutcome(expectedOutcome);
		return assertSeq;
	}
	
	public static SequenceGenerator<? extends Object> observeMethodOutput(Object receiver,
			String methodName, ValueGenerator<? extends Object> argGenerator) {
		//this produces the assertion like: assert(receiver.methodName(argPool) == ?)
		SMethod smethod = SMethod.instance(receiver, methodName);
		return new AssertionGenerator<SketchSequence>(receiver, smethod, argGenerator);
	}
	
    public static SequenceGenerator<? extends Object> observeMethodOutput(Object receiver,
    		String methodName, ValueGenerator<? extends Object> argGenerator, Object expectedResult) {
		//this produces the assertion like: assert(receiver.methodName(argPool).equals(expectedResult))
    	SMethod smethod = SMethod.instance(receiver, methodName);
    	AssertionGenerator<SketchSequence> assertSeq = new AssertionGenerator<SketchSequence>(receiver, smethod, argGenerator);
    	assertSeq.setExpectedObject(expectedResult);
    	//start generator
//    	assertSeq = GeneratorMaps.lookForGenerator(assertSeq);
    	
//    	boolean include = GeneratorMaps.containGenerator(assertSeq);
//    	System.out.println(include + " in oracle choice .");
    	SequenceGenerator<?> retGenerator = GeneratorMaps.lookForGenerator(assertSeq);
    	retGenerator.resetReceiver(receiver);
    	
    	if(retGenerator.current() != null) {
    	  //retGenerator.evaluate();
//    		ExecutableSequenceContent[] contents = retGenerator.executables();
//			Object[] results = SketchUtil.batchExecution(contents);
    		retGenerator.execute();
    	}
    	
    	//end generator
    	return retGenerator;
	}
    
    public static SequenceGenerator<? extends Object> observeMethodOutput(Object receiver,
    		String methodName, ValueGenerator<? extends Object> argGenerator, MethodExecOutcome expectedOutcome) {
    	//method execution outcome is an abstraction
		//according to the content of expectedOutcome, it could produce assertion like:
		//assert(receiver.methodName(argPool).equals(expectedOutcome.getResultObject())
		//try {
		//    receiver.methodName(argPool)
		//} catch (expectedException)
    	SMethod smethod = SMethod.instance(receiver, methodName);
    	AssertionGenerator<SketchSequence> assertSeq = new AssertionGenerator<SketchSequence>(receiver, smethod, argGenerator);
    	assertSeq.setExpectedOutcome(expectedOutcome);
    	return assertSeq;
	}
	
	public static SequenceGenerator<? extends Object> observeMethodOutputWithArgs(Object receiver,
			String methodName, ValueGenerator<Object[]> argPool) {
		//this produces the assertion like: assert(receiver.methodName(argPool) == ?)
		return null;
	}
	
    public static SequenceGenerator<? extends Object> observeMethodOutputWithArgs(Object receiver,
    		String methodName, ValueGenerator<Object[]> argPool, Object expectedResult) {
		//this produces the assertion like: assert(receiver.methodName(argPool).equals(expectedResult))
    	return null;
	}
    
    public static SequenceGenerator<? extends Object> observeMethodOutputWithArgs(Object receiver,
    		String methodName, ValueGenerator<Object[]> argPool, MethodExecOutcome expectedOutcome) {
    	//method execution outcome is an abstraction
		//according to the content of expectedOutcome, it could produce assertion like:
		//assert(receiver.methodName(argPool).equals(expectedOutcome.getResultObject())
		//try {
		//    receiver.methodName(argPool)
		//} catch (expectedException)
    	return null;
	}
	
	/**
	 * Sometimes, the produced object itself may not be interesting enough. Instead, we would be interested
	 * in calling its observe functions. For example, if a method produce a tree map, we may not be interested
	 * in whether it is null or not, instead, we are interested in calling its observe function, like size()
	 * to know the returned value, e.g.  receiver.methodName().observingMethod()
	 * */
	
    public static SequenceGenerator<? extends Object> observeMethodOutputValue(Object receiver, String methodName, ValueGenerator<Object[]> receiverArgPool,
    		String observingMethod, ValueGenerator<Object[]> observerArgPool) {
		//produce assertion like:  assert(receiver.method(args).observingMethod(arg) == ?)
    	return null;
	}
    
    public static SequenceGenerator<? extends Object> observeMethodOutputValue(Object receiver, String methodName, ValueGenerator<Object[]> receiverArgPool,
    		String observingMethod, ValueGenerator<Object[]> observerArgPool, Object expectedResult) {
		//produce assertion like:  assert(receiver.method(args).observingMethod(arg).equals(expectedResult))
    	return null;
	}

    public static SequenceGenerator<? extends Object> observeMethodOutputValue(Object receiver, String methodName, ValueGenerator<Object[]> receiverArgPool,
    		String observingMethod, ValueGenerator<Object[]> observerArgPool, MethodExecOutcome expectedOutcome) {
		//produce assertion like:  assert(receiver.method(args).observingMethod(arg)
    	//checks against the expectedOutcome object, to produce an assertion or
    	//wrap it with try/catch block
    	return null;
	}
}